﻿using System;

namespace CamlQueryCreator.Common
{
    /// <summary>
    /// Основной оператор CAML-запроса.
    /// </summary>
    public sealed class CamlOperatorCommon : CamlOperator
    {
        private readonly string _fieldName;
        private readonly FieldTypeKind _fieldType;
        private readonly object _fieldValue;
        private readonly OperationKind _operationKind;
        private readonly bool _isFindByID;

        /// <summary>
        /// Основной оператор CAML-запроса.
        /// <para>Конструктор.</para>
        /// </summary>
        private CamlOperatorCommon(CamlConnector parentBuilder, string fieldName, FieldTypeKind fieldType,
                                   object fieldValue, OperationKind operationKind, bool isFindByID)
            : base(parentBuilder)
        {
            _fieldName = fieldName;
            _fieldType = fieldType;
            _operationKind = operationKind;
            _isFindByID = isFindByID;

            if (fieldValue is bool && fieldType == FieldTypeKind.Boolean)
            {
                var value = (bool) fieldValue;
                _fieldValue = value ? 1 : 0;
            }
            else
            {
                _fieldValue = fieldValue;
            }
        }

        /// <summary>
        /// Основной оператор CAML-запроса.
        /// <para>Конструктор.</para>
        /// </summary>
        internal CamlOperatorCommon(CamlConnector parentBuilder, string fieldName, FieldTypeKind fieldType,
                                    object fieldValue, OperationKind operationKind)
            : this(parentBuilder, fieldName, fieldType, fieldValue, operationKind, true)
        {
        }

        /// <summary>
        /// Основной оператор CAML-запроса.
        /// <para>Конструктор.</para>
        /// </summary>
        internal CamlOperatorCommon(CamlConnector parentBuilder, IListFieldInfoBase fieldInfo, object fieldValue, OperationKind operationKind)
            : this(parentBuilder, fieldInfo.Name, fieldInfo.CamlQueryFieldType, fieldValue, operationKind)
        {
        }

        /// <summary>
        /// Основной оператор CAML-запроса к полю типа <see cref="FieldTypeKind.Lookup"/>.
        /// <para>Конструктор.</para>
        /// </summary>
        internal CamlOperatorCommon(CamlConnector parentBuilder, string fieldName, object fieldValue, OperationKind operationKind, bool isFindByID)
            : this(parentBuilder, fieldName, FieldTypeKind.Lookup, fieldValue, operationKind, isFindByID)
        {
        }

        #region Overrides of CamlBase

        /// <summary>
        /// Формирует запрос на основе итогового дочернего запроса и возвращает результат.
        /// </summary>
        protected override string GetQuery(string childQuery)
        {
            if (_fieldValue == null || _fieldValue.Equals(string.Empty))
                return string.Empty;

            return _fieldType == FieldTypeKind.Lookup || _fieldType == FieldTypeKind.User
                       ? GetEqLookup(_fieldName, _fieldValue, _operationKind, _isFindByID, _fieldType)
                       : GetEq(_fieldName, _fieldValue, _operationKind, _fieldType);
        }

        #endregion

        private static string GetEq(string fieldName, object fieldValue, OperationKind operationKind, FieldTypeKind fieldType)
        {
            const string eqFormat = "<{2}><FieldRef Name='{0}'/><Value Type='{3}'>{1}</Value></{2}>";

            if (fieldType == FieldTypeKind.DateTime && fieldValue is DateTime)
            {
                var value = (DateTime) fieldValue;
                return string.Format(eqFormat, fieldName, value.ToString("yyyy-MM-ddTHH:mm:ssZ"), operationKind.ToName(), fieldType);
            }

            return string.Format(eqFormat, fieldName, fieldValue, operationKind.ToName(), fieldType);
        }

        private static string GetEqLookup(string fieldName, object fieldValue, OperationKind operationKind, bool isFindByID, FieldTypeKind fieldType)
        {
            const string findByIdArrtibute = " LookupId='TRUE'";
            const string lookupFormat = 
                "<{2}><FieldRef Name='{0}'{3}/><Value Type='{4}'>{1}</Value></{2}>";

            return string.Format(
                lookupFormat,
                fieldName,
                fieldValue,
                operationKind.ToName(),
                isFindByID
                    ? findByIdArrtibute
                    : string.Empty,
                fieldType);
        }
    }
}
